package com.cainli.image;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.cookie.CookieSpec;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;

import android.util.Log;

public class HttpDownloader implements ProtocolDownloader
{

    private static final int BUFFER_SIZE = 4096;
    /**
     * HttpClient(单例)   
     */
    private static DefaultHttpClient sHttpClient = createHttpClient();

    @Override
    public File downloadImage(URL url,OutputStream out, Header[] headers, CookieStore cookies) throws IOException
    {
        try
        {
            HttpGet request = new HttpGet(url.toURI());
            if(cookies != null)
            {
                CookieSpec spec = sHttpClient.getCookieSpecs().getCookieSpec(
                        CookiePolicy.BEST_MATCH);
                List<Header> formatCookies = spec.formatCookies(cookies.getCookies());
                for (Header header : formatCookies)
                {
                    request.addHeader(header);
                }
            }
            if(headers != null && headers.length > 0)
            {
                for (Header header : headers)
                {
                    request.addHeader(header);
                }
            }
            
            // 这里有一个bug. 如果同时下载一张图片的话可能会出问题
            try
            {
                HttpResponse response = sHttpClient.execute(request);            
                int status = response.getStatusLine().getStatusCode();
                if(status != HttpStatus.SC_OK)
                {
                    throw new IOException(url + " response error! response code: " + status + " . reason: " + response.getStatusLine().getReasonPhrase());
                }
                
                HttpEntity entity = response.getEntity();
                // 写文件
                writeToFile(entity.getContent(), entity.getContentLength(), out);
                return null;            
            }
            finally
            {
                request.abort();
            }
            
        }
        catch (URISyntaxException e)
        {
            throw new IllegalArgumentException("illegal uri: " + url.toString());
        }
    }

    private void writeToFile(InputStream in, long fileLength, OutputStream dest) throws IOException
    {
        InputStream bis = new BufferedInputStream(in, BUFFER_SIZE);
        try
        {
            byte[] buffer = new byte[4096];
            int len;
            while ((len = bis.read(buffer)) != -1)
            {
                dest.write(buffer, 0, len);
            }            
        }
        finally
        {
            // 关闭流
            bis.close();
        }
    }
    
    /**
     * 创建httpClient
     * 
     * @return
     */
    private static DefaultHttpClient createHttpClient()
    {
        
        final SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        
        try
        {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
            SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);        
            registry.register(new Scheme("https", sf, 443));
        }
        catch (Exception e)
        {
            Log.e("HttpDownloader", "can't support http on this devices.");
        }
        ThreadSafeClientConnManager threadSafeClientConnManager = new ThreadSafeClientConnManager(
                new BasicHttpParams(), registry);
        return new DefaultHttpClient(threadSafeClientConnManager, null);
    }
    
    static class MySSLSocketFactory extends SSLSocketFactory {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
            super(truststore);

            TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };

            sslContext.init(null, new TrustManager[] { tm }, null);
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
        }

        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }
    }

    @Override
    public boolean useDiskCache()
    {
        return true;
    }

    @Override
    public Object decodeFile(File f)
    {
        return null;
    }

    @Override
    public boolean isDirty(URL url, long cacheTime)
    {
        return false;
    }

    @Override
    public boolean onPreDownload(URLDrawable drawable)
    {
        return false;
    }
    
}
